#include <QtTest/QtTest>
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include "../viterbi/Aminoacid.h"
#include "../viterbi/fileParser.h"
#include "../viterbi/Kodon.h"
#include "../viterbi/markov.h"
#include "../viterbi/fileParser.h"

#define TMP_DNA_FILE "tmp.tests.dna"
#define TMP_AMI_FILE "tmp.tests.ami"
#define TMP_DNA2_FILE "tmp.tests2.dna"
#define TMP_AMI2_FILE "tmp.tests2.ami"


#define sum_row(row,max,tab) sum = 0; for(int i = 0; i < max; ++i) sum += tab[row][i];
#define sum_col(col,max,tab) sum = 0; for(int i = 0; i < max; ++i) sum += tab[i][col];
#define set_s(ind,val) s[Kodon::asEnum(ind)]=std::log(val)

 class MarkovTests: public QObject
 {
     Q_OBJECT

 private:
     void markov_test_populate(const Markov m);
     void markov_test_generate(const Markov m);
     void markov_test_viterbi(const std::vector<int> result);


 private slots:
     void bootstrap();
     void aminoacid_data();
     void aminoacid();
     void kodon_toInt_good_data();
     void kodon_toInt_good();
     void kodon_toInt_bad_data();
     void kodon_toInt_bad();
     void kodon_toStr_data();
     void kodon_toStr();
     void fileParser_parse();
     void markov_test_all();

 };


 void MarkovTests::bootstrap()
 {
     int count;
     std::ofstream file;
     file.open (TMP_DNA_FILE, std::ios::out | std::ios::trunc);
     file << "uuc\n" << "agu\n" << "aga\n" << "uuc\n" << "uuu\n" << "uuu\n"<< "cug\n" << "agu\n" << "aga\n" << "uuc\n" << "aga\n" << "uuc\n" << "cug\n" << "agu\n";
     file.close();

     file.open(TMP_AMI_FILE, std::ios::out | std::ios::trunc);
     file << "phe\n" << "ser\n" << "arg\n" << "phe\n" << "phe\n" << "phe\n"<<"arg\n" << "ser\n" << "arg\n" << "phe\n" << "arg\n" << "phe\n" << "arg\n" << "ser\n";
     file.close();

     file.open (TMP_DNA2_FILE, std::ios::out | std::ios::trunc);
     file << "agu\n" << "aga\n" << "uuc\n" << "uuu\n" << "uuu\n" << "cug\n";
     file.close();

     file.open(TMP_AMI2_FILE, std::ios::out | std::ios::trunc);
     file << "ser\n" << "arg\n" << "phe\n" << "phe\n" << "phe\n" << "arg\n";
     file.close();

     std::ifstream dna_file(TMP_DNA_FILE);
     count = (int)std::count(std::istreambuf_iterator<char>(dna_file),
                             std::istreambuf_iterator<char>(), '\n');
     QCOMPARE(count,14);


     std::ifstream ami_file(TMP_AMI_FILE);
     count = (int)std::count(std::istreambuf_iterator<char>(ami_file),
                             std::istreambuf_iterator<char>(), '\n');
     QCOMPARE(count,14);
 }

 void MarkovTests::aminoacid_data()
 {
     int phe = Aminoacid::PHE, gly = Aminoacid::GLY, error = Aminoacid::ERROR;
     QTest::addColumn<QString>("ami_str");
     QTest::addColumn<int>("integer");

     QTest::newRow("good") << "phe" << phe;
     QTest::newRow("good2")     << "gly" << gly;
     QTest::newRow("bad case") << "Phe" << error;
     QTest::newRow("really bad") << "xxxxxxxxx" << error;
 }

 void MarkovTests::aminoacid()
 {
     QFETCH(QString, ami_str);
     QFETCH(int, integer);
     QCOMPARE((int)Aminoacid::asEnum(ami_str.toStdString(), false), integer);
 }

 void MarkovTests::kodon_toInt_good_data()
 {
     QTest::addColumn<QString>("kodon_str");
     QTest::addColumn<int>("integer");

     QTest::newRow("good")   << "aaa" << 0;
     QTest::newRow("good2")  << "act" << 7;
     QTest::newRow("good3")  << "tga" << 56;
     QTest::newRow("good4")  << "gct" << 39;
     QTest::newRow("good5")  << "aac" << 1;
 }

 void MarkovTests::kodon_toInt_good()
 {
     QFETCH(QString, kodon_str);
     QFETCH(int, integer);
     QCOMPARE((int)Kodon::asEnum(kodon_str.toStdString()), integer);
 }

 void MarkovTests::kodon_toInt_bad_data()
 {
     QTest::addColumn<QString>("kodon_str");

     QTest::newRow("bad case") << "Act";
     QTest::newRow("space") << " aaa";
     QTest::newRow("really bad") << "xxxxxxxxx";
 }

 void MarkovTests::kodon_toInt_bad()
 {
     QFETCH(QString, kodon_str);
     QCOMPARE((int)Kodon::asEnum(kodon_str.toStdString()) < 0, true);
 }

 void MarkovTests::kodon_toStr_data()
 {
     QTest::addColumn<int>("kodon_int");
     QTest::addColumn<QString>("kodon_str");


     QTest::newRow("good") << 0 << "aaa";
     QTest::newRow("good2") << 1 << "aac";
     QTest::newRow("good3") << 63 << "ttt";
     QTest::newRow("good4") << 6 << "acg";
     QTest::newRow("bad case") << 65 << "err";
     QTest::newRow("space") <<  123 << "err";
     QTest::newRow("really bad") << -123 << "err";
 }

 void MarkovTests::kodon_toStr()
 {
     QFETCH(int, kodon_int);
     QFETCH(QString, kodon_str);
     QCOMPARE(Kodon::asString(kodon_int),kodon_str.toStdString());
 }

 void MarkovTests::fileParser_parse()
 {
    FileParser fp;
    fp.switchToUseLongerSequenceFormat();
    std::vector<int> ret;

    ret = fp.parse(TMP_DNA_FILE);

    QCOMPARE((int)ret.size(),14);
    for(int i = 0; i < (int)ret.size();++i)
    {
        QCOMPARE((ret[i]>=0 && ret[i]<Kodon::COUNT),true);
    }

    ret = fp.parse(TMP_AMI_FILE);

    QCOMPARE((int)ret.size(),14);
    for(int i = 0; i < (int)ret.size();++i)
    {
        QCOMPARE((ret[i]>=0 && ret[i]<Aminoacid::COUNT),true);
    }
 }

 void MarkovTests::markov_test_all()
 {
    FileParser fp;
    fp.switchToUseLongerSequenceFormat();
    Markov m;

    m.populate(fp.parse(TMP_AMI_FILE),fp.parse(TMP_DNA_FILE));
    markov_test_populate(m);

    m.generate();
    markov_test_generate(m);

    double s[Kodon::COUNT];
    for(int i = 0; i<Kodon::COUNT; ++i) s[i] = 0;
    set_s("uuu",0.1); set_s("uuc",0.4); set_s("uca",0.05); set_s("agu",0.3); set_s("cug",0.05); set_s("aga",0.1);
    m.setPStart(s);


    markov_test_viterbi(m.viterbi(fp.parse(TMP_AMI2_FILE)));
 }

 void MarkovTests::markov_test_populate(const Markov m)
 {
     int sum,row;
     std::cout << "************************************\nEntering markov_test_populate()\n";
     const int (&A)[Kodon::COUNT][Kodon::COUNT] =  m.getA();
     const int (&E)[Kodon::COUNT][Aminoacid::COUNT] =  m.getE();

     row = Kodon::asEnum("uuu");
     sum_row(row,Kodon::COUNT,A);
     QCOMPARE(sum,2);
     sum_col(row,Kodon::COUNT,A);
     QCOMPARE(sum,2);

     row = Kodon::asEnum("uuc");
     sum_row(row,Kodon::COUNT,A);
     QCOMPARE(sum,4);
     sum_col(row,Kodon::COUNT,A);
     QCOMPARE(sum,3);

     row = Kodon::asEnum("agu");
     sum_row(row,Kodon::COUNT,A);
     QCOMPARE(sum,2);
     sum_col(row,Kodon::COUNT,A);
     QCOMPARE(sum,3);

     row = Kodon::asEnum("cug");
     sum_row(row,Kodon::COUNT,A);
     QCOMPARE(sum,2);
     sum_col(row,Kodon::COUNT,A);
     QCOMPARE(sum,2);

     row = Kodon::asEnum("aga");
     sum_row(row,Kodon::COUNT,A);
     QCOMPARE(sum,3);
     sum_col(row,Kodon::COUNT,A);
     QCOMPARE(sum,3);



     //int E [Kodon::COUNT][Aminoacid::COUNT];
     row = Aminoacid::asEnum("phe", false);
     sum_col(row,Kodon::COUNT,E);
     QCOMPARE(sum,6);

     row = Aminoacid::asEnum("ser", false);
     sum_col(row,Kodon::COUNT,E);
     QCOMPARE(sum,3);

     row = Aminoacid::asEnum("arg", false);
     sum_col(row,Kodon::COUNT,E);
     QCOMPARE(sum,5);



     row = Kodon::asEnum("uuu");
     sum_row(row,Aminoacid::COUNT,E);
     QCOMPARE(sum,2);

     row = Kodon::asEnum("uuc");
     sum_row(row,Aminoacid::COUNT,E);
     QCOMPARE(sum,4);

     row = Kodon::asEnum("uca");
     sum_row(row,Aminoacid::COUNT,E);
     QCOMPARE(sum,0);

     row = Kodon::asEnum("agu");
     sum_row(row,Aminoacid::COUNT,E);
     QCOMPARE(sum,3);

     row = Kodon::asEnum("cug");
     sum_row(row,Aminoacid::COUNT,E);
     QCOMPARE(sum,2);

     row = Kodon::asEnum("aga");
     sum_row(row,Aminoacid::COUNT,E);
     QCOMPARE(sum,3);

     std::cout << "Leaving markov_test_populate()\n";
 }

 void MarkovTests::markov_test_generate(const Markov m)
 {
     std::cout << "************************************\nEntering markov_test_generate()\n";
     std::cout << "Leaving markov_test_generate()\n";
 }

 void MarkovTests::markov_test_viterbi(const std::vector<int> result)
 {
     std::cout << "************************************\nEntering markov_test_viterbi()\n";
     FileParser fp;
     fp.switchToUseLongerSequenceFormat();
     int hit = 0,miss = 0;
     std::vector<int> original = fp.parse(TMP_DNA2_FILE);

     for(int i = 0; i < (int)result.size();++i)
     {
         result[i]==original[i]?++hit:++miss;
         std::cout << Kodon::asString(original[i]) << " -- " << Kodon::asString(result[i]) << "\n";
     }
     QCOMPARE(hit,6);

     QCOMPARE(miss,0);

     std::cout << "Leaving markov_test_viterbi()\n";
 }



 QTEST_MAIN(MarkovTests)
 #include "MarkovTests.moc"

